Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.

...powered by www.netzwerkartist.de...

 << zurück
Visual C# 2005 von Andreas Kühnel
Das umfassende Handbuch
Buch: Visual C# 2005

Visual C# 2005
1.320 S., mit 2 CDs, 59,90 Euro
Galileo Computing
ISBN 3-89842-586-X
gp Kapitel 22 Grafische Programmierung mit GDI+
  gp 22.1 Die Namespaces der GDI+-Schnittstelle
  gp 22.2 Die Klasse »Graphics«
    gp 22.2.1 Überblick über die Klasse »Graphics«
    gp 22.2.2 Sich die Referenz auf das »Graphics«-Objekt besorgen
    gp 22.2.3 Das Neuzeichnen einer Grafik mit »ResizeRedraw« und »Invalidate«
    gp 22.2.4 Zerstören von grafischen Objekten (Dispose)
    gp 22.2.5 Das Koordinatensystem von GDI+
    gp 22.2.6 Festlegen eines anderen Ursprungspunkts
    gp 22.2.7 Die grafischen Methoden der Klasse »Graphics«
    gp 22.2.8 Eine Linie zeichnen
    gp 22.2.9 Mehrere Linien zeichnen
    gp 22.2.10 Rechtecke zeichnen
    gp 22.2.11 Punkte zeichnen
    gp 22.2.12 Polygone zeichnen
    gp 22.2.13 Ellipsen, Ellipsenbogen und Ellipsensegment
    gp 22.2.14 Kurvenzüge
    gp 22.2.15 Bézierkurven
  gp 22.3 Elementare Klassen für grafische Operationen
    gp 22.3.1 Die Klasse »Brush«
    gp 22.3.2 Die Klasse »Pen«
    gp 22.3.3 Farbeinstellungen mit »Color«
  gp 22.4 Die Schriftdarstellung
    gp 22.4.1 Allgemeines
    gp 22.4.2 Die Klassen »Font« und »FontFamily«
    gp 22.4.3 Der Schriftstil mit »FontStyle«
    gp 22.4.4 Die grafische Ausgabe einer Zeichenfolge
    gp 22.4.5 Die Abmessungen mit »MeasureString« ermitteln
    gp 22.4.6 Die Klasse »StringFormat«
  gp 22.5 Bilddateien
    gp 22.5.1 Bilder und Grafiken der .NET-Klassenbibliothek
    gp 22.5.2 Die Bitmap-Dateiformate
    gp 22.5.3 Bilder mit der Klasse »Image«
    gp 22.5.4 Bitmaps


Galileo Computing

22.2 Die Klasse »Graphics«  downtop

Alle Objekte, auf die gezeichnet werden kann, haben ein objektspezifisches Graphics-Objekt. Sie können sich dieses bildlich wie eine Leinwand vorstellen, auf die gezeichnet wird. Die Form, die Picturebox und viele andere Typen haben jeweils einen eigenen Grafikkontext.


Galileo Computing

22.2.1 Überblick über die Klasse »Graphics«  downtop

Auf dem Graphics-Objekt werden die Zeichenoperationen ausgeführt. Dazu bietet Graphics zwei Methodengruppen an:

1.  Methoden, die mit dem Präfix Draw beginnen. Sie können damit Rechtecke zeichnen, aber auch Linien, Kreise, Polygone usw. Typische Vertreter dieser Gruppe sind DrawRectangle, DrawLine und DrawEllipse.
2. Methoden zum Füllen von Flächen von geometrischen Figuren. Diese Methodengruppe hat das Präfix Fill. FillEllipse und FillRectangle seien an dieser Stelle exemplarisch genannt.
       

Über die Zeichenoperationen hinaus beinhaltet Graphics auch viele Eigenschaften, die Informationen in Zusammenhang mit dem Grafikkontext bereitstellen.


Galileo Computing

22.2.2 Sich die Referenz auf das »Graphics«-Objekt besorgen  downtop

Das Besondere an Graphics ist, dass die Klasse keinen öffentlichen Konstruktor hat und deshalb nicht instanziiert werden kann. Um in ein bestimmtes Steuerelement zeichnen zu können, sind Sie also darauf angewiesen, sich die Referenz auf dessen Graphics-Objekt zu besorgen. Dazu bieten sich vornehmlich drei Möglichkeiten an:

gp  Codieren Sie das Paint-Ereignis einer Komponente, wird dem Ereignishandler ein Objekt vom Typ PaintEventArgs übergeben. Die Eigenschaft Graphics dieses Objekts liefert das Graphics-Objekt, das auf dem jeweiligem Control basiert.
gp  Sie besorgen sich die Referenz auf das Graphics-Objekt über die Methode CreateGraphics des entsprechenden Steuerelements.
gp  Die Klasse Graphics hat mit der statischen Methode FromHwnd selbst eine Alternative, die Graphics-Referenz einer Komponente in Erfahrung zu bringen.

Das »Paint«-Ereignis

Das Paint-Ereignis wird ausgelöst, wenn zumindest Teile eines Steuerelements neu gezeichnet werden müssen. Das ist auch der Fall, wenn das Steuerelement zum ersten Mal sichtbar wird oder wenn es durch andere Fenster verdeckt war. Deshalb werden Zeichenanweisungen vorzugsweise in Paint codiert. Paint wird aber auch dann ausgelöst, wenn sich die Größe des Formulars ändert. Standardmäßig werden dabei nur die Bereiche neu gezeichnet, die bei einer Vergrößerung hinzukommen. Grafiken, die abhängig von der Größe der Komponente sind, passen sich der Größenänderung nicht automatisch an, was zu sehr hässlichen Effekten führt. Wir werden darauf gleich noch einmal eingehen.

Sehen wir uns den Ereignishandler von Paint einer Form an:


private void Form1_Paint(object sender, PaintEventArgs e) {
  ...
}

Der zweite Parameter ist ein PaintEventArgs-Objekt, das uns in

gp  ClipRectangle
gp  Graphics

ereignisspezifische Daten bereitstellt. Graphics liefert die Referenz auf den Grafikkontext der Komponente, und ClipRectangle gibt das neu zu zeichnende Rechteck in seiner Breite und Höhe an. Der Rückgabewert ist vom Typ Rectangle.


public Rectangle ClipRectangle {get;}

Damit sind Sie in der Lage, die Zeichenoperationen auf die Bereiche zu beschränken, die ein Neuzeichnen erforden.

Das »Paint«-Ereignis mittels Code auslösen

Manchmal ist es erforderlich, alle Zeichenoperationen, die in Paint codiert sind, auf Anweisung hin ausführen zu lassen. Das könnte beispielsweise der Fall sein, wenn eine Komponente komplett neu gezeichnet werden muss. Dazu ist das programmgesteuerte Auslösen von Paint erforderlich. Hier hilft die Methode Invalidate weiter. Mit


pictureBox1.Invalidate();

wird das Paint-Ereignis eines Steuerelements namens pictureBox1 ausgelöst und der gesamte Bereich neu gezeichnet.

Bei aufwändigen Grafiken kann das eine unvertretbare Zeitspanne dauern. Deshalb gestatten die Überladungen von Invalidate die Angabe des Bereichs, der neu gezeichnet werden muss. Eine Überladung erwartet beispielsweise die Beschreibung durch die Angabe eines Rectangle-Objekts:


pictureBox1.Invalidate(new Rectangle(100, 100,
                       pictureBox1.ClientSize.Width – 100, 
                       pictureBox1.ClientSize.Height – 100);

Die »Graphics«-Referenz mit »CreateGraphics« ermitteln

Zeichenoperationen innerhalb des Paint-Ereignisses auszuführen, ist eine sehr einfache Angelegenheit, da der Ereignishandler über den zweiten Parameter die Referenz auf den Grafikkontext bereitstellt. Manchmal möchte man aber auch außerhalb des Paint-Ereignisses einer Komponente in diese zeichnen. Dann ist es notwendig, sich die Graphics-Referenz des entsprechenden Elements zu besorgen.

Jetzt hilft die parameterlose Methode CreateGraphics weiter, die auf die Objektvariable des Steuerelements aufgerufen wird. Der Rückgabewert ist das Graphics-Objekt, auf das anschließend die Zeichenroutinen aufgerufen werden können.


public Graphics CreateGraphics();

Möchten Sie, dass das Klicken auf einen Button bewirkt, ein Rechteck in die Form zu zeichnen, könnte der Code wie folgt aussehen:


private void button1_Click(object sender, System.EventArgs e) {
  Graphics graph = this.CreateGraphics();
  graph.DrawRectangle(new Pen(Brushes.Blue, 10), 20, 20, 100, 100);
}

Die Methode DrawRectangle des Graphics-Objekts beschreibt hier ein Quadrat, das in einer Strichstärke von 10 Pixeln gezeichnet wird. Der Ursprungspunkt des Quadrats (die Ecke links oben) hat die je 20 Pixel Abstand vom Rand des Arbeitsbereichs der Form. Die Seitenlänge des Quadrats beträgt 100 Pixel.

Die »Graphics«-Referenz mit »FromHwnd« ermitteln

Eine Alternative zu CreateGraphics wird durch die statische Methode FromHwnd der Klasse Graphics beschrieben.


public static Graphics FromHwnd(IntPtr hwnd);

Die Methode erwartet als Argument einen Handle auf die Komponente, in die gezeichnet werden soll. Diesen stellt die Eigenschaft Handle bereit, die in der Klasse Control definiert ist und von allen Steuerelementen geerbt wird.

Angenommen, in einer Form würde sich eine PictureBox namens pictureBox1 befinden und wir wollen auf das Anklicken einer Schaltfläche hin in dieses Steuerelement eine Diagonale zeichnen, so müssten wir den Ereignishandler von Click wie folgt implementieren:


private void button1_Click(object sender, EventArgs e) {
  Graphics gr = Graphics.FromHwnd(pictureBox1.Handle);
  gr.DrawLine(new Pen(Brushes.Black), 0, 0, 100, 100);
}

Beispielprogramm

Wir wollen uns nun an einem Beispielprogramm ansehen, wie Graphics dazu benutzt wird, einfache geometrische Figuren in eine Form und eine Picturebox zu zeichnen. Dazu benutzen wir exemplarisch die beiden Graphics-Methoden DrawEllipse und FillRectangle, um in der Arbeitsfläche einer Form eine Ellipse und ein farblich gefülltes Rechteck darzustellen. Beide erwarten neben den Positions- und Größenangaben im ersten Argument auch noch ein Objekt, mit dem die Farbe beschrieben wird, in der das geometrische Objekt gezeichnet werden soll. Bei DrawEllipse handelt es sich um ein Pen-Objekt, bei FillRectangle um ein Objekt vom Typ SolidBrush. Diese beiden Klassen werden später noch vorgestellt.

In der Form ist ein PictureBox-Control enthalten. Darin wird ebenfalls eine Ellipse gezeichnet. Da diese jedoch eine Füllfarbe haben soll, bietet sich die Methode FillEllipse an. Außerdem wird vom Ursprung des Koordinatensystems der Picturebox bis zur untersten rechten Ecke eine Linie mit DrawLine gezeichnet.

Abbildung
Hier klicken, um das Bild zu vergrößern

Abbildung 22.1   Ausgabe des Beispiels »EinfacheGrafiken«


// --------------------------------------------------------------
// Beispiel: ...\Kapitel 22\EinfacheGrafiken
// --------------------------------------------------------------
private void Form1_Paint(object sender, PaintEventArgs e) {
  e.Graphics.DrawEllipse(new Pen(Color.Blue), 30,10,120,70);
  e.Graphics.FillRectangle(new SolidBrush(Color.Red), 20, 110, 35, 69);
}
private void pictureBox1_Paint(object sender, PaintEventArgs e) {
  e.Graphics.FillEllipse(new SolidBrush(Color.Azure), 0,10,120,70);
  e.Graphics.DrawLine(new Pen(Color.Black), 0, 0, 
    pictureBox1.ClientSize.Width, pictureBox1.ClientSize.Height);
}


Galileo Computing

22.2.3 Das Neuzeichnen einer Grafik mit »ResizeRedraw« und »Invalidate«  downtop

Wird eine Grafik teilweise verdeckt, beispielsweise durch eine andere Form, und anschließend wieder sichtbar, wird nicht die gesamte Grafik neu gezeichnet, sondern nur der Teil, der verdeckt war. Genauso verhält es sich auch, wenn eine Form vergrößert wird.

Sind die grafischen Routinen abhängig von der Größe des Fensters, tritt ein Effekt auf, wie Sie ihn in Abbildung 22.2 sehen können.

Abbildung
Hier klicken, um das Bild zu vergrößern

Abbildung 22.2   Grafische Routinen, die von der Formgröße abhängig sind

Den Abbildungen liegt der folgende Code im Paint-Ereignis der Form zugrunde. Beachten Sie, dass sich die Größe der Ellipsen an den Abmessungen des Clientbereichs der Form orientiert.


private void Form1_Paint(object sender, PaintEventArgs e) {
  Graphics gr = e.Graphics;
  for (int i = 0; i <5; i++) {
    gr.DrawEllipse(new Pen(Brushes.Black), 
        i * this.ClientSize.Width / 10,
        i * this.ClientSize.Height / 10,
        this.ClientSize.Width – 2 * this.ClientSize.Width / 10 * i,
        this.ClientSize.Height – 2 * this.ClientSize.Height / 10 * i);
  }
}

Wird das Fenster zur Laufzeit vergrößert, wird Paint mehrfach ausgelöst mit der Folge, dass immer nur die neu hinzugekommenen Bereiche gezeichnet werden. Das Resultat sehen Sie im rechten Fenster der Abbildung – es sieht dilettantisch aus.

Mit der Eigenschaft ResizeRedraw der Form und der Einstellung true können Sie festlegen, dass bei jeder Größenänderung der gesamte Bereich der Form neu gezeichnet werden soll. Es bietet sich an, diese Eigenschaft, die nicht im Eigenschaftsfenster angezeigt wird, beispielsweise im Konstruktor der Form zu setzen:


public Form1() {
  InitializeComponent();
  this.ResizeRedraw = true;
}

ResizeRedraw ist in System.Windows.Forms.Control folgendermaßen definiert:


protected bool ResizeRedraw {get; set;}

Der Zugriffsmodifizierer protected hat Konsequenzen, denn er erlaubt nur den Zugriff aus der Klasse selbst heraus oder aus einer Klasse, die von Control abgeleitet ist. Bei der benutzerdefinierten Form ist das der Fall, weil diese von der Basisklasse Form abgeleitet ist, die ihrerseits die Klasse Control beerbt.

Etwas anderes ist es, wenn wir unsere Betrachtungen auf eine Picturebox projizieren, denn mit


private void Form1_Paint(object sender, PaintEventArgs e) {
  pictureBox1.ResizeRedraw = true; // falsch !!!
}

befinden wir uns nicht innerhalb der Picturebox und haben deshalb auch nur auf die öffentlichen Member Zugriff. ResizeRedraw ist aber nicht öffentlich.

Wie können wir nun ein Neuzeichnen der Picturebox oder jedes anderen grafikfähigen Steuerelements initiieren? Der Anstoß dazu erfolgt durch Aufruf der Invalidate-Methode, die in der Klasse Control definiert ist und folglich von allen Steuerelementen geerbt wird.


public void Invalidate();

Invalidate ist überladen und gestattet auch, nur spezifizierte Bereichen neu zeichnen zu lassen.

Im folgenden Beispiel ResizeRedrawDemo finden Sie den Code des obigen Codefragments wieder, mit dem in einer Form Ellipsen gezeichnet werden. Das Beispiel ist um den Ereignishandler DoubleClick der Form ergänzt, mit dem die ResizeRedraw-Eigenschaft zur Laufzeit umgeschaltet werden kann, um das Projekt nicht immer wieder neu starten zu müssen.


// --------------------------------------------------------------
// Beispiel: ...\Kapitel 22\ResizeRedrawDemo
// --------------------------------------------------------------
private void Form1_Paint(object sender, PaintEventArgs e) {
  Graphics gr = e.Graphics;
  for (int i = 0; i <5; i++)  {
    gr.DrawEllipse(new Pen(Brushes.Black), i * this.ClientSize.Width / 10,
         i * this.ClientSize.Height / 10,
         this.ClientSize.Width – 2 * this.ClientSize.Width / 10 * i,
         this.ClientSize.Height – 2 * this.ClientSize.Height / 10 * i);
   }
}
private void Form1_DoubleClick(object sender, ventArgs e) {
   this.ResizeRedraw = !this.ResizeRedraw;
   this.Text = "ResizeRedraw = " + this.ResizeRedraw.ToString();
}


Galileo Computing

22.2.4 Zerstören von grafischen Objekten (Dispose)  downtop

Während des Bereinigungsprozesses ruft der Garbage Collector (GC) im Destruktor automatisch die Dispose-Methode eines Objekts auf, wenn die Klasse die Schnittstelle IDisposable implementiert. Wann der GC seinen Dienst aufnimmt, ist nicht vorhersehbar. Das kann sich nachteilig auswirken, wenn die Speicherressourcen knapp werden: Das System verlangsamt sich.

Insbesondere Anwendungen, die eine sehr große Anzahl grafischer Objekte erzeugen, sind davon betroffen, denn grafische Objekte verbrauchen meist sehr viele Speicherressourcen. Generell ist es so, dass der GC seine Arbeit aufnimmt, wenn sich die Ressourcen verknappen. Auf der anderen Seite verlangsamt sich das System dadurch in einem Ausmaß, das wirklich nicht akzeptabel ist.

Ein kleines Beispiel soll das verdeutlichen.


private void button1_Click(object sender, EventArgs e) {
  DateTime starttime = DateTime.Now;
  Bitmap bmp;
  for (int i = 0; i < 1000; i++) {
    bmp = new Bitmap(1000, 1000);
    bmp.Dispose();
  }
  TimeSpan difftime = DateTime.Now.Subtract(starttime);
  this.Text = string.Format("{0}.{1} Sekunden", 
                     difftime.Seconds, difftime.Milliseconds);
}

In einer Schleife werden hier 1000 Bitmap-Objekte in einer Größe von 1000 x 1000 Pixel erzeugt. Die Zeit, die für diese Operation benötigt wird, wird gemessen und der Titelleiste der Form angezeigt. Die Zeitspanne, die für das Erzeugen der Bitmaps benötigt wird, schwankt natürlich in Abhängigkeit von der Hardware-Ausstattung des Systems. Auf dem Rechner des Autors betrug sie knapp acht Sekunden – wenn in der Schleife jedes neue Bitmap-Objekt mit Dispose sofort aufgegeben wird. Ein Auskommentieren von Dispose hatte eine Verdopplung der Operationszeit zur Folge.


Hinweis   Besorgen Sie sich die Referenz auf ein Graphics-Objekt mit CreateGraphics oder FromHwnd, werden Sie vermutlich die zurückgelieferte Referenz in einer Objektvariablen speichern. Sobald Sie diese nicht mehr brauchen, sollte auf diese Objektvariable Dispose aufgerufen werden.


Galileo Computing

22.2.5 Das Koordinatensystem von GDI+  downtop

Bevor wir uns mit den grafischen Methoden der Klasse Graphics eingehender beschäftigen, müssen wir das zugrunde liegende GDI+-Koordinatensystem verstehen, auf das die Grafikroutinen aufsetzen. Dieses weist standardmäßige Einstellungen auf, sonst könnten wir auch die Methoden des Typs Graphics nicht einsetzen. Das Koordinatensystem lässt sich aber nach eigenen Wünschen festlegen, sei es die Umstellung der Maßeinheit oder die alternative Festlegung eines anderen Koordinatenursprungs.

Die Maßeinheit des Koordinatensystems

Als Standardmaßeinheit gelten Pixel. Mit der Eigenschaft PageUnit des Graphics-Objekts können wir diese Einstellung anders festlegen oder die aktuelle Einstellung abrufen.


public GraphicsUnit PageUnit {get; set;}

Der Eigenschaft können die folgenden Mitglieder der GraphicsUnit-Enumeration zugewiesen werden:


Tabelle 22.1   Die Enumeration »GraphicsUnit«

Member Beschreibung
Display 1/100 Zoll
Document 1/300 Zoll
Inch Zoll (2,54 cm)
Millimeter Millimeter
Pixel Bildschirmpixel
Point 1/72 Zoll

Das folgende Codefragment sowie Abbildung 22.3 zeigen die Auswirkungen einer Änderung der Maßeinheit vom Standard GraphicsUnit.Pixel in GraphicsUnit.Point anhand zweier Rechtecke, deren Abmessung und Position identisch ist. Zur besseren optischen Unterscheidung ist das erste Rechteck mit der Farbe Color.Azure gefüllt, das zweite wird nur durch die schwarzen Randlinien dargestellt.


private void Form1_Paint(object sender, PaintEventArgs e) {
  // FillRectangle basiert auf GraphicsUnit.Pixel (Standard)
  e.Graphics.FillRectangle(new SolidBrush(Color.Azure), 10, 10, 100, 100);
  e.Graphics.PageUnit = GraphicsUnit.Point;
  e.Graphics.DrawRectangle(new Pen(Color.Black), 10, 10, 100, 100);
}

Abbildung
Hier klicken, um das Bild zu vergrößern

Abbildung 22.3   Einfluss der Eigenschaft »PageUnit« auf die Darstellung

Skalierung der Grafikausgabe

Die Eigenschaft PageScale des Graphics-Objekts dient dazu, die grafische Ausgabe um einen bestimmten Faktor zu skalieren. Verwenden Sie die beiden Eigenschaften PageUnit und PageScale parallel, multipliziert sich die Wirkung entsprechend der eingestellten Werte. In Abbildung 22.4 können Sie die Auswirkungen von PageScale sehen, wenn der Skalierfaktor 2 beträgt. Beachten Sie, dass sich auch die Linienbreite entsprechend verändert.


// Standardwert
e.Graphics.DrawRectangle(new Pen(Color.Black),5,5,50,50);
e.Graphics.PageScale = (float)2;
e.Graphics.DrawRectangle(new Pen(Color.Black),5,5,50,50);

Abbildung
Hier klicken, um das Bild zu vergrößern

Abbildung 22.4   Einfluss der Eigenschaft »PageScale« auf die Darstellung


Galileo Computing

22.2.6 Festlegen eines anderen Ursprungspunkts  downtop

Standardmäßig liegt der Ursprungspunkt in der linken oberen Ecke des Clientbereichs (0, 0). Die positive Richtung der x-Achse zeigt dabei nach rechts, die positive Richtung der y-Achse nach unten und unterscheidet sich damit von der mathematischen, die nach oben zeigt.

Mit den XxxTransform-Methoden der Graphics-Klasse lässt sich das Koordinatensystem unterschiedlich beeinflussen.


public void TranslateTransform(float, float);

Mit TranslateTransform wird das Koordinatensystem um die in den beiden Parametern übergebenen Werte verschoben. Mit


graph.TranslateTransform(10, –30);

sind das zehn Einheiten in positiver x-Richtung und 30 Einheiten in negativer Richtung.

Die Methode ScaleTransform skaliert das Koordinatensystem sowohl in x- als auch in y-Richtung:


public void ScaleTransform(float, float);

Diese Methode ähnelt der Methode PageScale. Der Unterschied ist, dass Textausgaben mit PageScale nicht verändert werden. Mit ScaleTransform passen sich die gezeichneten Schriften dem Skalierungsfaktor an.

Wollen Sie das Koordinatensystem um einen bestimmten Winkel um seinen Ursprungspunkt (0, 0) drehen, müssen Sie den Drehwinkel der Methode RotateTransform übergeben:


public void RotateTransform(float);

Der Winkel wird in Grad angegeben. Die positive Drehrichtung ist im Uhrzeigersinn.

Wenn Sie das Koordinatensystem wieder in seine ursprüngliche Ausgangsposition zurückversetzen wollen, können Sie alle Transformationen mit ResetTransform zurücksetzen:


public void ResetTransform();

Beispielprogramm der Verschiebung des Koordinatensystems

Wir wollen uns nun die Verschiebung des Koordinatensystems in einem Beispielprogramm ansehen (siehe Abbildung 22.5). In einer Picturebox sollen die Achsen des Koordinatensystems in der Farbe Rot gezeichnet werden. Über zwei Schieberegler können sowohl die x- als auch die y-Achse innerhalb der Clientfläche beliebig verschoben werden. Ausgehend vom Ursprung des Koordinatensystems wird ein Rechteck mit einem schwarzen Rahmen gezeichnet, dessen Position sich bezüglich des Koordinatensystems nicht ändert. Es wird also in gleichem Maße mitverschoben.

Abbildung
Hier klicken, um das Bild zu vergrößern

Abbildung 22.5   Fenster des Beispiels »Transformation«


// --------------------------------------------------------------
// Beispiel: ...\Kapitel 22\Transformation    
// --------------------------------------------------------------
private void Form1_Load(object sender, EventArgs e) {
  trackY.Maximum = picBox.ClientSize.Width;
  trackX.Maximum = picBox.ClientSize.Height;
  trackY.TickFrequency = (int)trackY.Maximum / 10;
  trackX.TickFrequency = (int)trackX.Maximum / 10;
}
private void picBox_Paint(object sender, PaintEventArgs e) {
  Graphics g = e.Graphics;
  // neues Koordinatensystem festlegen
  g.TranslateTransform(trackX.Value, trackY.Value);
  // x-Koordinate neu zeichnen
  g.DrawLine(new Pen(Color.Red), -trackX.Value, 0, 
                 picBox.ClientSize.Width – trackX.Value, 0);
  // y-Koordinate zeichnen
  g.DrawLine(new Pen(Color.Red), 0, -trackY.Value, 0,   
                 picBox.ClientSize.Height – trackY.Value);
  // Rechteck zeichnen
  g.DrawRectangle(new Pen(Color.Black), 0, 0, 100, 100);
}
// Gemeinsamer Ereignishandler der Scroll-Ereignisse beider
// TrackBar-Steuerelemente
private void track_Scroll(object sender, EventArgs e) {
  picBox.Invalidate();
}

Die grafischen Operationen des Beispiels sind im Ereignis Paint codiert. Zuerst wird mit


g.TranslateTransform(trackX.Value, trackY.Value);

das Koordinatensystem in die Position verschoben, die durch die Stellung der beiden Schieberegler trackX und trackY beschrieben wird. Alle Operationen, die auf das Graphics-Objekt ausgeführt werden, orientieren sich an der neuen Position des Koordinatensystems. In unserem Beispiel ist das der zweimalige Aufruf der Methode Drawline sowie DrawRectangle.

Bei jeder Auslösung des Paint-Ereignisses wird die Arbeitsfläche des zugrunde liegenden Steuerelements neu gezeichnet. Grafische Elemente, die vorher in dem Steuerelement zu sehen waren, werden damit gleichzeitig auch verworfen. Das erleichtert uns die Programmierung ganz wesentlich, denn bei einer Verschiebung des Koordinatensystems legen wir keinen Wert mehr darauf, die alte Position zu sehen.

Das Scroll-Ereignis des Schiebereglers tritt auf, wenn dieser durch eine Maus- oder Tastaturaktion bewegt wird. Da unsere Grafikroutinen im Paint-Ereignis der Picturebox implementiert sind, müssen wir nur noch eine passende Möglichkeit finden, dieses Ereignis auszulösen. Genau dies leistet die Invalidate-Methode der Picturebox.


Galileo Computing

22.2.7 Die grafischen Methoden der Klasse »Graphics«  downtop

Die Klasse Graphics ist die elementarste Klasse für grafische Operationen. Sie stellt Methoden bereit, mit denen die unterschiedlichsten geometrischen Figuren gezeichnet werden können. Wir wollen uns nun diese Methoden ansehen, die zugleich auch meist vielfach überladen sind.


Tabelle 22.2   Grafische Methoden der Klasse »Graphics«

Methode Beschreibung
DrawArc Zeichnet einen Ellipsenbogen.
DrawBezier Zeichnet eine durch vier Point-Strukturen definierte Bézier-Splinekurve.
DrawBeziers Zeichnet eine Reihe von Bézier-Splinekurven aus einem Array von Point-Strukturen.
DrawClosedCurve Zeichnet eine geschlossene Cardinal-Splinekurve, die durch ein Array von Point-Strukturen definiert ist.
DrawCurve Zeichnet eine Cardinal-Splinekurve durch ein angegebenes Array von Point-Strukturen.
DrawEllipse Zeichnet eine Ellipse in einem umschließenden Rechteck.
DrawLine Zeichnet eine Linie zwischen zwei Punkten.
DrawLines Zeichnet mehrere Linien auf Basis eines Point-Arrays.
DrawPie Zeichnet ein Ellipsensegment.
DrawPolygon Zeichnet die Kontur eines Polygons.
DrawRectangle Zeichnet ein Rechteck anhand der Eckpunkte.
DrawRectangles Zeichnet mehrere Rechtecke anhand eines Rectangle-Arrays.
DrawString Zeichnet eine Zeichenfolge an einer bestimmten Position.
FillEllipse Zeichnet eine farbgefüllte Ellipse in einem umschließenden Rechteck.
FillClosedCurve Zeichnet eine farbgefüllte geschlossene Cardinal-Splinekurve, die durch ein Array von Point-Strukturen definiert ist.
FillPie Zeichnet ein farbgefülltes Ellipsensegment.
FillPolygon Zeichnet die farbgefüllte Kontur eines Polygons.
FillRectangle Zeichnet ein farblich ausgefülltes Rechteck anhand der Eckpunkte.
FillRectangles Zeichnet mehrere farblich ausgefüllte Rechtecke anhand eines Rectangle-Arrays.


Galileo Computing

22.2.8 Eine Linie zeichnen  downtop

Um eine Linie darzustellen, steht die vierfach überladene Methode DrawLine zur Verfügung. Als Erstes erwarten alle Überladungen ein Argument vom Typ Pen, mit dem die Darstellung der zu zeichnenden Linie beschrieben wird. Das ist zumindest die Farbe, kann aber auch zusätzlich die Linienbreite sein.

Eine Linie beschreibt sich zwischen einem Anfangs- und Endpunkt. Sie haben die Wahl, ob Sie die Koordinaten der Punkte einzeln als int oder float übergeben wollen oder als eine geordnete Point-Struktur.


private void Form1_Paint(object sender, PaintEventArgs e) {
  e.Graphics.DrawLine(new Pen(Brushes.Red, 6), 12, 20, 120, 200);
}


Galileo Computing

22.2.9 Mehrere Linien zeichnen  downtop

Mit DrawLines lässt sich ein Linienverbund zeichnen. Die Methode erwartet ebenfalls die Referenz auf ein Pen-Objekt sowie zusätzlich ein Array von Point-Objekten. Die Anzahl der Array-Elemente (= Punkte) kann ungerade sein, da der Endpunkt der einen Linie zum Anfangspunkt der nächsten Linie wird. Das Ergebnis ist eine Kette von miteinander in Verbindung stehenden Linien, wobei das geometrische Gebilde nicht automatisch geschlossen wird.


private void Form1_Paint(object sender, PaintEventArgs e) {
  PointF[] points = {new Point(0, 0), new Point(25, 35),
                      new Point(120, 50), new Point(25, 130)};
  e.Graphics.DrawLines(new Pen(Color.Black, 2), points);
}

In der folgenden Abbildung sehen Sie das Ergebnis der Zeichenoperation.

Abbildung
Hier klicken, um das Bild zu vergrößern

Abbildung 22.6   Linien, gezeichnet mit der »DrawLines«-Methode


Galileo Computing

22.2.10 Rechtecke zeichnen  downtop

Die Parameterlisten der DrawRectangle-Methoden ähneln denen der DrawLine-Methode. Der erste Parameter erwartet ein Pen-Objekt, den folgenden Parametern werden zwei Punkte übergeben, welche die Koordinaten des linken oberen und rechten unteren Rechtecks beschreiben. Alternativ können Sie auch eine Rectangle-Struktur übergeben.

 


private void Form1_Paint(object sender, PaintEventArgs e) {
  e.Graphics.DrawRectangle(new Pen(Brushes.Blue), 0, 0, 100, 60);
}

Benötigen Sie mehrere Rechtecke, die sich weder in Farbe noch Linie unterscheiden, bietet sich die Methode DrawRectangles an, die ein Array der Struktur Rectangle erwartet. Alle im Array enthaltenen Rechtecke werden in derselben Farbe und Strichstärke gezeichnet.


private void Form1_Paint(object sender, PaintEventArgs e) {
  Rectangle[] rectArr = {new Rectangle(0, 0, 20, 40),
                         new Rectangle(8, 15, 45, 70),
                         new Rectangle(15, 20, 80, 120)};
  e.Graphics.DrawRectangles(new Pen(Brushes.Black), rectArr);
}

Mit DrawRectangle und DrawRectangles zeichnen Sie Rechtecke, deren Innenflächenfarbe der Hintergrundfarbe des Containers entspricht. Wollen Sie farblich ausgefüllte Rechtecke zeichnen, müssen Sie auf die alternativen Methoden FillRectangle bzw. FillRectangles zurückgreifen. Diesen wird im ersten Parameter kein Pen-Objekt übergeben, sondern ein Brush-Objekt. Da die Klasse Brush abstrakt ist und nicht unmittelbar verwendet werden kann, wird im Code die Referenz auf eine von Brush abgeleitete Klasse übergeben. Damit ist es auch beispielsweise möglich, anstelle einer Farbfüllung eine beliebige Schraffur zu wählen. Brush und die daraus abgeleiteten Klassen werden uns im Abschnitt 22.3.1 beschäftigen.

Die Strukturen »Rectangle« und »RectangleF«

Wir sollten auch noch einen Blick auf die Konstruktoren der Struktur Rectangle werfen, um uns darüber zu informieren, welche Möglichkeiten uns dieser Typ bietet, ein Rechteck zu zeichnen.


public Rectangle(Point, Size);
public Rectangle(int, int, int, int);

Im ersten Konstruktor wird der linke obere Punkt des Rechtecks durch eine Point-Struktur beschrieben, Size beschreibt die Breite und Höhe des Rechtecks. Alternativ ist auch die Angabe der Koordinaten des linken oberen oder rechten unteren Eckpunkts des Rechtecks möglich.

Beispielcode

Der folgende Beispielcode demonstriert den Einsatz einiger DrawRectangle-Methoden. Das Ergebnis ist in der Abbildung 22.7 zu sehen.

Abbildung
Hier klicken, um das Bild zu vergrößern

Abbildung 22.7   Rechtecke mit den Methoden »DrawRectangle« und »FillRectangle«


private void Form1_Paint(object sender, PaintEventArgs e) {
  for (int i = 0; i <10; i++){
    Rectangle rect = new Rectangle(i*10, i*7, 50, 30);
    e.Graphics.DrawRectangle(new Pen(Color.Black),rect);
  }
  // Koordinatensystem verschieben
  e.Graphics.TranslateTransform(this.ClientSize.Width/2,0);
  for (int i = 0; i <10; i++) {
    Rectangle rect = new Rectangle(i*10, i*7, 50, 30);
    // Rechteck mit roter Füllfarbe zeichnen
    e.Graphics.FillRectangle(new SolidBrush(Color.Red),rect);
    // einfaches Rechteck mit dickerem schwarzen Rahmen zeichnen      
    e.Graphics.DrawRectangle(new Pen(Color.Black, 2),rect);
  }
}

Zur Farbfüllung wird in diesem Code eine Instanz der Klasse SolidBrush benutzt. Damit werden Innenflächen einfarbig gefüllt.


Galileo Computing

22.2.11 Punkte zeichnen  downtop

Wenn Sie sich Tabelle 22.2 ansehen, werden Sie feststellen, dass darin keine Methode enthalten ist, um einen einzelnen Punkt zu setzen. Es gibt dazu zwei Lösungswege:

gp  Sie benutzen die Methode FillRectangle mit in allen Punkten identischen Koordinatenwerten.
gp  Sie verwenden die Klasse Bitmap und legen mit der Methode SetPixel einen Punkt fest.

Galileo Computing

22.2.12 Polygone zeichnen  downtop

Die Methode DrawPolygon ähnelt der Methode DrawLines und kann diese sogar ohne weitere Änderungen im Programmcode ersetzen. Der Unterschied zwischen diesen beiden Methoden ist, dass mit DrawPolygon die letzte Linie als schließende Linie gezeichnet wird.


private void Form1_Paint(object sender, PaintEventArgs e) {
  Point[] pArr = {new Point(2, 7), 
                  new Point(25, 12), 
                  new Point(67, 100)};
  e.Graphics.DrawPolygon(new Pen(Brushes.Black, 2), pArr);
}


Galileo Computing

22.2.13 Ellipsen, Ellipsenbogen und Ellipsensegment  downtop

Gezeichnet wird eine Ellipse innerhalb eines umschließenden Rechtecks, daher gleichen sich auch die Parameterlisten von DrawRectangle und DrawEllipse. Im folgenden Codefragment wird eine Ellipse in die Form gezeichnet, deren umschließendes Rechteck im Ursprungspunkt des Clientbereichs der Form beginnt. Die Breite beträgt 200 Pixel, die Höhe 100 Pixel.


private void Form1_Paint(object sender, PaintEventArgs e) {
  e.Graphics.DrawEllipse(new Pen(Brushes.Blue, 3), 0, 0, 200, 100);
}

Benötigen Sie einen Kreis- bzw. Ellipsenbogen, bietet sich DrawArc an. Diese Methode baut auf einer Ellipse auf, deren umschließendes Rechteck dem Methodenaufruf übergeben wird. Zudem müssen Sie dem vorletzten Parameter den Startwinkel zwischen der x-Achse und dem Anfangspunkt des Bogens mitteilen sowie dem letzten Parameter den Winkel des zu zeichnenden Bogens. Die Winkel werden in Grad und im Uhrzeigersinn angegeben.

Dazu ein Beispiel. Die Ausgabe dazu sehen Sie in Abbildung 22.8.


Pen pen= new Pen(Color.Black, 2);
e.Graphics.DrawArc(pen, 0, 0, 100, 100, 45, 270);
e.Graphics.DrawArc(pen, 150, 0, 100, 100, 45, 90); 

Abbildung
Hier klicken, um das Bild zu vergrößern

Abbildung 22.8   Ellipsenbögen, gezeichnet mit »DrawArc«

Die überladene DrawPie-Methode funktioniert genauso wie DrawArc, zeichnet aber anstelle eines Ellipsenbogens ein Ellipsensegment.


Pen pen= new Pen(Color.Black, 2);
e.Graphics.DrawPie(pen, 0, 0, 100, 100, 45, 270);
e.Graphics.DrawPie(pen, 150, 0, 100, 100, 45, 90);

Abbildung
Hier klicken, um das Bild zu vergrößern

Abbildung 22.9   Ellipsenbögen, gezeichnet mit »DrawArc«

Da mit DrawEllipse und DrawPie geschlossene geometrische Figuren dargestellt werden, gibt es parallel dazu auch die Methoden FillEllipse und FillPie, um eine Farbfüllung zu erreichen.


Galileo Computing

22.2.14 Kurvenzüge  downtop

Mit den Methoden DrawCurve und DrawClosedCurve lassen sich Kurven (Cardinal-Splinekurven) zeichnen, die durch in einem Point-Array definierte Punkte führen. Mit DrawClosedCurve wird der Kurvenzug geschlossen. Beide Methoden sind mehrfach überladen. Mehrere Methoden erwarten einen Parameter vom Typ float, dem man mitteilen kann, wie nah eine Kurve den Punkten folgen soll. Ist dieser Wert 0, haben wir es nur noch mit Geraden zu tun, also einem Polygonzug.

In Abbildung 22.10 sehen Sie die beiden Kurven, die mit dem folgenden Programmcode gezeichnet werden. Die im Point-Array definierten Punkte sind durch einen kleinen Kreis optisch hervorgehoben.


private void Form1_Paint(object sender, PaintEventArgs e) {
  Point[] pt = new Point[]{new Point(0,4),
                           new Point(50,150),
                           new Point(100,70),
                           new Point(150,155),
                           new Point(200,145),
                           new Point(250,160),
                           new Point(300,20)};
  e.Graphics.DrawCurve(new Pen(Brushes.Red,3),pt);
  e.Graphics.DrawCurve(new Pen(Brushes.Black),pt, 1);
  for (int i = 0; i < 7; i++)
    e.Graphics.DrawEllipse(new Pen(Brushes.Black,2),
                          pt[i].X – 2, pt[i].Y – 3, 6, 6);
}

Abbildung
Hier klicken, um das Bild zu vergrößern

Abbildung 22.10   Kurven, gezeichnet mit den »DrawCurve«-Methoden


Galileo Computing

22.2.15 Bézierkurven  toptop

Mit DrawBezier lässt sich eine Kurve zwischen Punkten zeichnen. Zwischen diesen beiden Punkten werden zwei Stützpunkte definiert. Diese beeinflussen die Kurve in der Weise, dass vom ersten Stützpunkt eine Tangente an die durch den Startpunkt gehende Kurve gelegt werden kann. Analog kann vom zweiten Stützpunkt eine Tangente an die durch den Endpunkt gehende Kurve gelegt werden.

Die Tatsache, dass insgesamt vier Punkte den Verlauf der Bézierkurve festlegen (Startpunkt, Endpunkt, zwei Stützpunkte), macht sich auch in der Parameterliste bemerkbar, die vier Point-Objekte erwartet:


Point[] pt = new Point[]{new Point(10, 100),
                         new Point(30, 30),
                         new Point(150, 150),
                         new Point(300, 100)};
e.Graphics.DrawBezier(new Pen(Brushes.Black), pt[0], pt[1], pt[2], pt[3]);

Das erste Point-Objekt ist der Startpunkt, danach folgen die beiden Stützpunkte, und der vierte Point-Parameter beschreibt den Endpunkt.

In Abbildung 22.11 wird zuerst eine Linie zwischen einem Start- und einem Endpunkt gezeichnet. Die Bézierkurve wird durch die beiden ober- und unterhalb liegenden Stützpunkte auf den gewünschten Verlauf »gezogen«. Die Tangenten sind gepunktet dargestellt.

Abbildung
Hier klicken, um das Bild zu vergrößern

Abbildung 22.11   Bézierkurve

Der Abbildung liegt der folgende Programmcode zugrunde:


private void Form1_Paint(object sender, PaintEventArgs e) {
  Point[] pt = new Point[]{new Point(10, 100),
                           new Point(30, 30),
                           new Point(150, 150),
                           new Point(300, 100)};
  // Gerade zwischen Start- und Endpunkt
  e.Graphics.DrawLine(new Pen(Brushes.Black, 2),pt[0], pt[3]);
  // Bezierkurve
  e.Graphics.DrawBezier(new Pen(Brushes.Black),
                         pt[0], pt[1], pt[2], pt[3]);
  Pen tgPen = new Pen(Brushes.Black,2);
  tgPen.DashStyle = DashStyle.Dot;
  // Tangente vom 1. Stützpunkt an die Kurve im Startpunkt
  e.Graphics.DrawLine(tgPen,pt[0],pt[1]);
  // Tangente vom 2. Stützpunkt an die Kurve im Endpunkt
  e.Graphics.DrawLine(tgPen,pt[2],pt[3]);
  // Punkte markieren
  for (int i = 0; i < 4; i++)
    e.Graphics.DrawEllipse(new Pen(Brushes.Black, 1),
  pt[i].X – 2, pt[i].Y – 3, 6, 6);
}

 << zurück
  
  Zum Katalog
Zum Katalog: Visual C# 2005
Visual C# 2005
bestellen
 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchtipps
Zum Katalog: Fortgeschrittene Programmierung mit Visual C# 2005






 Fortgeschrittene
 Programmierung
 mit Visual C# 2005


Zum Katalog: Einstieg in Visual C# 2005






 Einstieg in
 Visual C# 2005


Zum Katalog: Einstieg in Visual Basic 2005






 Einstieg in
 Visual Basic 2005


Zum Katalog: Visual Basic 2005






 Visual Basic 2005


Zum Katalog: Java ist auch eine Insel






 Java ist auch eine
 Insel


Zum Katalog: Konzepte und Lösungen für Microsoft-Netzwerke






 Konzepte und
 Lösungen für
 Microsoft-Netzwerke


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo








Copyright © Galileo Press 2006
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de